/* This file is part of Reactive Cascade which is released under The MIT License. See license.md , https://github.com/futurice/cascade and http://reactivecascade.com for details. This is open source for the common good. Please contribute improvements by pull request or contact paulirotta@gmail.com */ package com.reactivecascade.util; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.support.annotation.NonNull; import android.test.suitebuilder.annotation.MediumTest; import com.reactivecascade.AsyncAndroidTestCase; import com.reactivecascade.functional.SettableAltFuture; import org.junit.Before; import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import static com.reactivecascade.Async.UI; import static com.reactivecascade.Async.WORKER; public class UIExecutorServiceTest extends AsyncAndroidTestCase { final Object looperFlushMutex = new Object(); volatile int handleMessageCount; volatile int dispatchMessageCount; volatile int sendCount; volatile UIExecutorService uiExecutorService; private Thread fakeUiThread; @Before @Override public void setUp() throws Exception { if (fakeUiThread == null) { fakeUiThread = new HandlerThread("FakeUiHandler", Thread.NORM_PRIORITY) { protected void onLooperPrepared() { uiExecutorService = new UIExecutorService(new Handler() { public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); handleMessageCount++; } /** * Handle system messages here. */ public void dispatchMessage(@NonNull Message msg) { super.dispatchMessage(msg); dispatchMessageCount++; } public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) { sendCount++; return super.sendMessageAtTime(msg, uptimeMillis); } }); } }; fakeUiThread.start(); while (true) { if (uiExecutorService != null) { break; } Thread.yield(); } } super.setUp(); } protected void flushLooper() throws InterruptedException { synchronized (looperFlushMutex) { uiExecutorService.execute(() -> { synchronized (looperFlushMutex) { RCLog.v(UIExecutorServiceTest.this, "Looper flushed"); looperFlushMutex.notifyAll(); } }); looperFlushMutex.wait(); } } @MediumTest public void testUIIsShutdown() throws Exception { assertFalse(UI.isShutdown()); } @MediumTest public void testIsTerminated() throws Exception { assertFalse(uiExecutorService.isTerminated()); } @MediumTest public void testSubmitCallable() throws Exception { uiExecutorService.submit(new Callable<Object>() { @Override public Object call() throws Exception { return null; } }); flushLooper(); assertEquals(2, sendCount); } @MediumTest public void testSubmitRunnable() throws Exception { uiExecutorService.submit(new Runnable() { @Override public void run() { } } ); flushLooper(); assertEquals(2, sendCount); } @MediumTest public void testInvokeAllCallable() throws Exception { AtomicInteger ai = new AtomicInteger(0); ArrayList<Callable<Integer>> callableList = new ArrayList<>(); SettableAltFuture<String> saf = new SettableAltFuture<>(WORKER); callableList.add(() -> { ai.set(100); return 100; }); callableList.add(() -> { ai.set(ai.get() + 200); return 200; }); callableList.add(() -> { saf.set("done"); return 1; }); uiExecutorService.invokeAll(callableList); awaitDone(saf); assertTrue(sendCount > 0); assertEquals(300, ai.get()); } @MediumTest public void testInvokeAllCallableTimeout() throws Exception { AtomicInteger ai = new AtomicInteger(0); ArrayList<Callable<Integer>> callableList = new ArrayList<>(); SettableAltFuture<String> saf = new SettableAltFuture<>(WORKER); callableList.add(() -> { ai.set(100); return 100; }); callableList.add(() -> { ai.set(ai.get() + 200); return 200; }); callableList.add(() -> { saf.set("done"); return 1; }); uiExecutorService.invokeAll(callableList, 1000, TimeUnit.MILLISECONDS); awaitDone(saf); assertTrue(sendCount > 0); assertEquals(300, ai.get()); } @MediumTest public void testInvokeAnyCallable() throws Exception { AtomicInteger ai = new AtomicInteger(0); ArrayList<Callable<Integer>> callableList = new ArrayList<>(); SettableAltFuture<String> saf = new SettableAltFuture<>(WORKER); callableList.add(() -> { ai.set(100); return 100; }); callableList.add(() -> { ai.set(ai.get() + 200); return 200; }); callableList.add(() -> { saf.set("done"); return 1; }); uiExecutorService.invokeAny(callableList); awaitDone(saf); assertTrue(sendCount > 0); assertTrue(ai.get() > 0); } @MediumTest public void testInvokeAnyCallableTimeout() throws Exception { AtomicInteger ai = new AtomicInteger(0); ArrayList<Callable<Integer>> callableList = new ArrayList<>(); SettableAltFuture<String> saf = new SettableAltFuture<>(WORKER); callableList.add(() -> { ai.set(100); return 100; }); callableList.add(() -> { ai.set(ai.get() + 200); return 200; }); callableList.add(() -> { saf.set("done"); return 1; }); uiExecutorService.invokeAny(callableList, 1000, TimeUnit.MILLISECONDS); awaitDone(saf); assertTrue(sendCount > 0); assertTrue(ai.get() > 0); } @MediumTest public void testExecute() throws Exception { final AtomicInteger ai = new AtomicInteger(0); WORKER.execute(() -> { uiExecutorService.execute(() -> { ai.set(100); }); }); long endTime = System.currentTimeMillis() + 1000; while (!(ai.get() == 100)) { if (System.currentTimeMillis() > endTime) { throw new TimeoutException(); } Thread.yield(); } } }